The struct representing an upload configuration.
Overview
The Phoenix.LiveView.UploadConfig module defines the configuration structure for file uploads in LiveView. It is created by allow_upload/3 and manages upload entries, validation, and processing.
Type Definition
@type t :: %Phoenix.LiveView.UploadConfig{
name: atom() | String.t(),
cid: :unregistered | nil | integer(),
client_key: String.t(),
max_entries: pos_integer(),
max_file_size: pos_integer(),
entries: list(),
entry_refs_to_pids: %{String.t() => pid() | :unregistered | :done},
entry_refs_to_metas: %{String.t() => map()},
accept: list() | :any,
acceptable_types: MapSet.t(),
acceptable_exts: MapSet.t(),
external: function() | false,
allowed?: boolean(),
errors: list(),
ref: String.t(),
auto_upload?: boolean(),
writer: function(),
progress_event: function() | nil
}
Struct Fields
The upload name provided to allow_upload/3
Unique reference for this upload configuration
List of upload entries (files)
Maximum number of files allowed. Default: 1
Maximum file size in bytes. Default: 8,000,000 (8MB)
Size of chunks for upload streaming. Default: 64,000 bytes
Timeout for chunk uploads in milliseconds. Default: 10,000ms
List of accepted file types or :any. Examples: [".jpg", ".png", "image/*"]
Set of acceptable MIME types
Set of acceptable file extensions
External upload function for third-party storage, or false for local uploads
Whether the upload is currently allowed
List of validation errors as {ref, error} tuples
Whether files should be uploaded automatically. Default: false
Optional callback for upload progress updates
Function that returns the writer module and options for handling uploads
Error Types
Upload validation can produce the following errors:
File exceeds max_file_size
Number of files exceeds max_entries
File type not in the accept list
Error during external upload
Default Values
@default_max_entries 1
@default_max_file_size 8_000_000 # 8 MB
@default_chunk_size 64_000 # 64 KB
@default_chunk_timeout 10_000 # 10 seconds
Usage
The upload config is created with allow_upload/3:
def mount(_params, _session, socket) do
socket =
socket
|> allow_upload(:avatar,
accept: ~w(.jpg .jpeg .png),
max_entries: 1,
max_file_size: 5_000_000
)
{:ok, socket}
end
Access the config in your assigns:
def render(assigns) do
~H"""
<%= for entry <- @uploads.avatar.entries do %>
<div>
<%= entry.client_name %> - <%= entry.progress %>%
</div>
<% end %>
<%= for err <- @uploads.avatar.errors do %>
<div class="error"><%= error_to_string(err) %></div>
<% end %>
"""
end
The :accept option supports:
File Extensions
Must start with a period and have a known MIME type:
accept: ~w(.jpg .jpeg .png .gif)
MIME Types
accept: ["image/jpeg", "image/png"]
MIME Type Wildcards
accept: ["image/*", "video/*", "audio/*"]
Any File Type
External Uploads
For uploads to external storage (S3, GCS, etc.):
def mount(_params, _session, socket) do
socket =
socket
|> allow_upload(:avatar,
accept: ~w(.jpg .jpeg .png),
max_entries: 1,
external: &presign_upload/2
)
{:ok, socket}
end
defp presign_upload(entry, socket) do
{:ok, meta, socket} =
Phoenix.LiveView.Upload.external_upload_meta(entry, socket)
# Generate presigned URL for your storage service
presigned_url = MyApp.Storage.generate_presigned_url(entry, socket)
{:ok, %{uploader: "S3", url: presigned_url}, socket}
end
Custom Writer
Provide a custom writer for advanced upload handling:
def mount(_params, _session, socket) do
socket =
socket
|> allow_upload(:avatar,
accept: :any,
writer: fn _name, _entry, _socket ->
{MyApp.CustomUploadWriter, [bucket: "uploads"]}
end
)
{:ok, socket}
end
Progress Callback
Track upload progress with a custom callback:
def mount(_params, _session, socket) do
socket =
socket
|> allow_upload(:documents,
accept: ["application/pdf"],
progress: &handle_progress/3
)
{:ok, socket}
end
defp handle_progress(:documents, entry, socket) do
if entry.done? do
# Upload complete
{:noreply, put_flash(socket, :info, "Upload complete!")}
else
# Still uploading
{:noreply, socket}
end
end
Auto Upload
Enable automatic upload on file selection:
def mount(_params, _session, socket) do
socket =
socket
|> allow_upload(:avatar,
accept: ~w(.jpg .jpeg .png),
auto_upload: true
)
{:ok, socket}
end
Validation Example
def error_to_string(:too_large), do: "File is too large"
def error_to_string(:too_many_files), do: "You have selected too many files"
def error_to_string(:not_accepted), do: "File type not accepted"
def render(assigns) do
~H"""
<form phx-change="validate" phx-submit="save">
<.live_file_input upload={@uploads.avatar} />
<%= for entry <- @uploads.avatar.entries do %>
<div>
<progress value={entry.progress} max="100"><%= entry.progress %>%</progress>
<button type="button" phx-click="cancel-upload" phx-value-ref={entry.ref}>×</button>
</div>
<% end %>
<%= for {_ref, error} <- @uploads.avatar.errors do %>
<p class="error"><%= error_to_string(error) %></p>
<% end %>
<button type="submit">Upload</button>
</form>
"""
end
def handle_event("validate", _params, socket) do
{:noreply, socket}
end
def handle_event("cancel-upload", %{"ref" => ref}, socket) do
{:noreply, cancel_upload(socket, :avatar, ref)}
end
def handle_event("save", _params, socket) do
uploaded_files =
consume_uploaded_entries(socket, :avatar, fn %{path: path}, entry ->
dest = Path.join("uploads", entry.client_name)
File.cp!(path, dest)
{:ok, ~p"/uploads/#{entry.client_name}"}
end)
{:noreply, socket |> put_flash(:info, "Files uploaded!")}
end